
#ifndef LOD_COMPARE_DISP_H
#define LOD_COMPARE_DISP_H

// todo
#include <iostream>
using std::cout;
using std::endl;
using std::flush;

#include <sstream>
#include <string>
#include <fstream>

#include <assert.h>

#include <GL/glew.h>
//#include <GL/glut.h>
//#include <GL/gl.h>

#include "../types.h"
#include "../base/material.h"
#include "disp.h"

#include "../jeff/mesh.h"
#include "../jeff/pmesh.h"
#include "../light/lighting.h"
#include "../jeff_camera.h"



class compare_disp : public disp
{
    public:
    compare_disp(void* font, const GLfloat* menu_color4, const GLfloat* act_menu_color4, 
              int Width, int Height)
     : disp(font, menu_color4, act_menu_color4, 
             compare_disp::text, compare_disp::yinc_text, compare_disp::xoffset_text, 
             compare_disp::show_text, compare_disp::menu_pos,
             compare_disp::text_count,
             compare_disp::help_str, compare_disp::help_count),
       width(Width),
       height(Height),
       ob_loaded(false),
       typing(false),
       jmesh(NULL),
       jpmesh_q(NULL),
       jpmesh_qt(NULL),
       jpmesh_m(NULL),
       jpmesh_s(NULL),
       do_q(false),
       do_qt(true),
       do_m(false),
       do_s(false),
       do_orig(true),
       tr_perc(5),
       display_ob(false)
    {
        //reshape(width, height);
        set_act_menu_pos(START_ACT_MENU_POS);
        //set_show_help(HELP_SHOWED_ON_START);
        show_help=false;
        tr_perc_adjust_menu();
        assert(num_chosen_methods()<=4); // protects us from making a mistake 
                                        // in the default values couple of lines above
    }

    virtual ~compare_disp()
    {
        if(jmesh!=NULL)
            delete jmesh;
        if(jpmesh_q!=NULL)
            delete jpmesh_q;
        if(jpmesh_qt!=NULL)
            delete jpmesh_qt;
        if(jpmesh_m!=NULL)
            delete jpmesh_m;
        if(jpmesh_s!=NULL)
            delete jpmesh_s;
    }

     
    virtual void display(int width, int height)
    {

        if(ob_loaded)
        {
            if(show_help==false)
            {
                if(display_ob) 
                {
                    int moveright = (int)(width/6);
                    if(polygon_mode)
                        glPolygonMode( GL_FRONT, GL_FILL );
                    else
                        glPolygonMode( GL_FRONT, GL_LINE );

                    if(illuminated)
                        li.enable();
                    else {
                        li.disable();
                        grey();
                    }

                    glMatrixMode(GL_MODELVIEW);
                    glLoadIdentity();
                    gluLookAt(0,0,jcam.get_dist(), 0,0,0, 0,1,0);
                    glRotatef(jcam.get_elevation(),1,0,0);
                    glRotatef(jcam.get_azimuth(),0,1,0);


                    if(num_chosen_methods()==1)
                    {
                        glViewport(0,0,width,height);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                                     //45
                        gluPerspective(60, (GLdouble)width / (GLdouble)height, 0.1, 30.0);
                        
                        //display_jmesh(jmesh);
                        display_num_jmesh(0);
                        // draw a number of triangles 
                        li.disable();
                        quick_draw_string(width,height,get_method_name(0).c_str(),20,60);
                        quick_draw_string(width,height,
                            ("t: "+get_num_triangles_str(0)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(0))+"%").c_str(),20,40);
                        
                    }
                    else if(num_chosen_methods()==2)
                    {
                        int w = (width-moveright);
                        int h = height/2;
                        glViewport(moveright,h,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);

                        display_num_jmesh(0);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(0).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(0)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(0))+"%").c_str(),20,20);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        //glViewport(moveright,0,w,h);
                        w = width;
                        glViewport(0,0,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);
                        display_num_jmesh(1);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(1).c_str(),20,60);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(1)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(1))+"%").c_str(),20,40);
                    }
                    else if(num_chosen_methods()==3)
                    {
                        int w = (width-moveright);
                        int h = height/2;
                        glViewport(moveright,h,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);

                        display_num_jmesh(0);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(0).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(0)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(0))+"%").c_str(),20,20);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        //w /= 2;
                        //glViewport(moveright,0,w,h);
                        w = width/2;
                        glViewport(0,0,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);

                        display_num_jmesh(1);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(1).c_str(),20,60);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(1)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(1))+"%").c_str(),20,40);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        //glViewport(moveright+w,0,w,h);
                        glViewport(w,0,w,h);
                        display_num_jmesh(2);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(2).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(2)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(2))+"%").c_str(),20,20);
                    }
                    else if(num_chosen_methods()==4)
                    {
                        int w = (width-moveright)/2;
                        int h = height/2;
                        glViewport(moveright,h,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);

                        display_num_jmesh(0);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(0).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(0)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(0))+"%").c_str(),20,20);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        glViewport(moveright+w,h,w,h);
                        display_num_jmesh(1);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(1).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(1)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(1))+"%").c_str(),20,20);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        //glViewport(moveright,0,w,h);
                        w = width/2;
                        glViewport(0,0,w,h);
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity();
                        gluPerspective(60, (GLdouble)w / (GLdouble)h, 0.1, 30.0);
                        display_num_jmesh(2);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(2).c_str(),20,60);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(2)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(2))+"%").c_str(),20,40);
                        if(illuminated)
                            li.enable();
                        else
                            grey();

                        //glViewport(moveright+w,0,w,h);
                        glViewport(w,0,w,h);
                        display_num_jmesh(3);
                        li.disable();
                        quick_draw_string(w,h,get_method_name(3).c_str(),20,40);
                        quick_draw_string(w,h,("t: "+get_num_triangles_str(3)+" ~ "+
                             get_2dig_percent_str(get_num_triangles(3))+"%").c_str(),20,20);
                    }
                } // display_ob==true

                li.disable();
                glViewport(0,0,width,height);
                // draw the loaded object's name
                quick_draw_string(width,height,("Object: "+ob_name).c_str(),20,20);

            }// show_help==false
            else {
                li.disable();
                glViewport(0,0,width,height);
                disp_help(width,height);
            }

            comp_type emph_col[4] = {0.8, 0.5, 0.9, 1.0};
            disp_text(width, height, build_emph_vec(), emph_col);
        }
        else
        {
            li.disable();
            glViewport(0,0,width,height);
            disp_text(width, height);
        }
    }

    // todo zmienic biorac pod uwage ob_loaded (wykluczyc pewne funkcje gdy ob_loaded==false)
    virtual disp* keyboard(unsigned char key, int x, int y)
    {
        disp* menu = this;
        if(typing) 
        {
            switch(key) {
                // strange observation: glut gives just '\r'
                // as a newline character under Linux
                case '\r':
                    this->enter_key();
                    break;
                case '\b':
                    backspace_key();
                    break;
                case 27: // escape
                    menu = escape_key();
                    break;
                default:
                    key_typed(key);
            }
        }
        else // typing==false
        {
            switch(key) {
                // strange observation: glut gives just '\r'
                // as a newline character under Linux
                case '\r':
                    this->enter_key();
                    break;
                case '\b':
                    backspace_key();
                    break;
                case 27: // escape
                    menu = escape_key();
                    break;

                case 'w':
                    this->act_menu_pos_up();
                    break;
                case 's':
                    this->act_menu_pos_down();
                    break;

                case 'd': // display object on/off
                    display_ob=!display_ob;
                    break;
                case 'S':
                    li.swap_shading(); // switch between flat/smooth shading
                    break;
                case 'l': // turn on or off the lights
                    illuminated = !illuminated;
                    break;
                case 'f':   // wireframe/filled
                    polygon_mode = !polygon_mode;
                    break;

                case 'g': // collapse edges
                    if(ob_loaded)
                        collapse();
                    break;
                case 'u': // split vertices
                    if(ob_loaded)
                        split();
                    break;

                case 'h':
                    show_help = !show_help;
                    break;

                default:
                    ;
            }
        }
        return menu;
    }

    virtual void keyboard_special(int key, int x, int y)
    {
        if(typing==false) {
            switch(key) {
                case GLUT_KEY_UP:
                    this->act_menu_pos_up();
                    break;
                case GLUT_KEY_DOWN:
                    this->act_menu_pos_down();
                    break;
            }
        }
        // if the user's typing we discard the special keys
    }

    virtual void reshape(int width, int height)
    {
        if(ob_loaded==false) {
            set_xstart_text(int(width * 1/2.5));
            set_ystart_text(int(height * 1/2.0));
        }
        else {
            set_xstart_text(15);
            set_ystart_text(height-30);
        }
        // store the updated values of width and height
        if(width!=this->width || height!=this->height) {
            this->width = width;
            this->height = height;
        }
    }

    virtual void init()
    {
        // viewport and projection m. are set in display()
        // (multiple viewports used)
        initOpenGL();
    }

    virtual void mouse(int button, int state, int x, int y)
    {
        if(button==GLUT_LEFT_BUTTON) {
            lmb_state = state;
            if(state==GLUT_DOWN)
                rmb_state = GLUT_UP;    // restrict that only one button is pressed
        }
        else if(button==GLUT_RIGHT_BUTTON) {
            rmb_state = state;
            if(state==GLUT_DOWN)
                lmb_state = GLUT_UP;
        }
    }

    virtual void motion(int x, int y)
    {
        jcam.mouseMotion(x,y,width,height, lmb_state==GLUT_DOWN, rmb_state==GLUT_DOWN);
    }

    virtual void passive_motion(int x, int y)
    {
        jcam.mouseMotion(x,y,width,height, false, false);
    }




    private:
    void enter_key()
    {
        if(ob_loaded==false) {
            typing = !typing;
            if(typing==true) {
                start_typing();
            }
            else//(typing==false)
            {
                stop_typing();
                if(typed.length()>0) 
                    load_ob(typed);
                typed.clear();
            }
        }
        else { // ob_loaded==true
            int pos = get_act_menu_pos();
            switch(pos) 
            {
                // case 0 and case 1 represent the same menu position,
                // but just in different states
                case 0: // load an object (start typing)
                    typing=!typing;
                    assert(typing);
                    start_typing();
                    break;
                case 1: // load the object (that the name of which you have typed)
                    typing=!typing;
                    assert(!typing);
                    stop_typing();
                    if(typed.length()>0)
                        load_ob(typed);
                    typed.clear();
                    break;

                    // choose simplification methods (maximum 4 methods)
                case 3: // quadric simpl. method
                    //if(do_q || num_chosen_methods()<4)
                    //    do_q=!do_q;
                    if(do_q)
                        do_q=!do_q;
                    else
                        if(num_chosen_methods()<4) {
                            do_q=!do_q;
                            rebuild_jpmeshes();
                        }
                    break;
                case 4: // quadric simpl. method (weighted by the area of the triangles)
                    //if(do_qt || num_chosen_methods()<4)
                    //    do_qt=!do_qt;
                    if(do_qt)
                        do_qt=!do_qt;
                    else
                        if(num_chosen_methods()<4) {
                            do_qt=!do_qt;
                            rebuild_jpmeshes();
                        }
                    break;
                case 5: // Stan Melax's simpl. method
                    //if(do_m || num_chosen_methods()<4)
                    //    do_m=!do_m;
                    if(do_m)
                        do_m=!do_m;
                    else
                        if(num_chosen_methods()<4) {
                            do_m=!do_m;
                            rebuild_jpmeshes();
                        }
                    break;
                case 6: // shortest edge first method
                    //if(do_s || num_chosen_methods()<4)
                    //    do_s=!do_s;
                    if(do_s)
                        do_s=!do_s;
                    else
                        if(num_chosen_methods()<4) {
                            do_s=!do_s;
                            rebuild_jpmeshes();
                        }
                    break;
                case 7: // display original
                    if(do_orig || num_chosen_methods()<4)
                        do_orig=!do_orig;
                    break;

                case 9:
                    typing=!typing;
                    assert(typing);
                    start_typing();
                    break;
                case 10:
                    typing=!typing;
                    assert(!typing);
                    stop_typing();
                    if(typed.length()>0) {
                        save_tr_percent(typed);
                        update_tr_num_from_perc();
                        tr_num_adjust_menu();
                    }
                    tr_perc_adjust_menu();
                    // it's certain that ob_loaded==true (we are inside "else" block)
                    typed.clear();
                    break;
                case 11:
                    typing=!typing;
                    if(typing)
                        start_typing();
                    else {
                        stop_typing();
                        if(typed.length()>0) {
                            save_tr_num(typed);
                            tr_num_adjust_menu();
                            update_tr_perc_from_num();
                            tr_perc_adjust_menu();
                        }
                        
                        typed.clear();
                    }
                    break;
                case 12: // edge collapse (generate coaser LOD)
                    if(ob_loaded)
                        collapse();
                    break;
                case 13: // vertex split (restore more details)
                    if(ob_loaded)
                        split();
                    break;
            }
        }
    }

    // return the number of chosen (active) methods
    int num_chosen_methods()
    {
        int n = 0;
        if(do_q) n++;
        if(do_qt) n++;
        if(do_m) n++;
        if(do_s) n++;
        if(do_orig) n++;
        return n;
    }

    std::vector<int> build_emph_vec()
    {
        std::vector<int> v;
        if(do_q)
            v.push_back(3);
        if(do_qt)
            v.push_back(4);
        if(do_m)
            v.push_back(5);
        if(do_s)
            v.push_back(6);
        if(do_orig)
            v.push_back(7);
        return v;
    }

    void backspace_key()
    {
        if(typing && typed.length()>0) 
        {
            std::string base;
            int p = get_act_menu_pos();
            if(p==1)  // load an object
                base = std::string(text[1]);
            else if(p==10)
                base = std::string(text[10]);
            else if(p==11)
                base = std::string(text[11]);
            else {
                ;
                assert(false);  // that's why we have all this long if block
            }
            
            typed = typed.substr(0, typed.length()-1);
            std::string s = std::string(base) + typed;
            change_text(p, s.c_str());  // and this time shorter, we avoid using the long block
                                        // by using position p
        }
    }

    disp* escape_key()
    {
        disp* menu = this;
        if(typing) {
            typing = false;
            typed.clear();
            
            stop_typing();
        }
        else
            menu = change_menu(false); // go back
        return menu;
    }

    
    void start_typing()
    {
        if(ob_loaded==false) {
            menu_pos_visible(0, false);
            menu_pos_visible(1, true);
            set_act_menu_pos(1);
        }
        else {
            int p = get_act_menu_pos();
            if(p==0) { // load an object
                menu_pos_visible(0, false);
                menu_pos_visible(1, true);
                set_act_menu_pos(1);
            }
            else if(p==9) { // percent of the triangles to remove
                menu_pos_visible(9, false);
                menu_pos_visible(10, true);
                set_act_menu_pos(10);
            }
            else if(p==11) { // a number of triangles to remove
                change_text(11, text[11]);
            }
        }
    }

    
    void stop_typing()
    {
        if(ob_loaded==false) {
            menu_pos_visible(0, true);
            menu_pos_visible(1, false);
            change_text(1, text[1]);
            set_act_menu_pos(0);
        }
        else {
            int p = get_act_menu_pos();
            if(p==1) { // load an object
                menu_pos_visible(0, true);
                menu_pos_visible(1, false);
                change_text(1, text[1]); // bring back the base label
                set_act_menu_pos(0);
            }
            else if(p==10) { // percent of triangles to remove
                menu_pos_visible(9, true);
                menu_pos_visible(10, false);
                change_text(10, text[10]);
                set_act_menu_pos(9);
            }
            else if (p==11) { // a number of triangles to remove
                tr_num_adjust_menu();
                // we changed a bit the concept of handling the menu in this case;
                // there is exactly one position, so visibility and focus remains
                // we only take care about the correct text displayed (which will
                // change soon if the number of triangles to remove has changed,
                // but this is handled in another place)
            }
        }
    }


    /*! we call this method only when the user is typing some value
    // (as opposed to e.g. switching by the user some display options
    // by typing one-key shortcuts) */
    void key_typed(unsigned char key)
    {
        if(ob_loaded==false) {
            typed += key;
            std::string s = std::string(text[1]) + typed;
            change_text(1, s.c_str());
        }
        else { // ob_loaded==true
            int p = get_act_menu_pos();
            if(p==1) { // load an object
                typed += key;
                std::string s = std::string(text[1]) + typed;
                change_text(1, s.c_str());
            }
            else if(p==10) { // percent of triangles to remove
                if(key=='.' && typed.find('.')==std::string::npos ||
                   key>='0' && key<='9') {
                    typed += key;
                    std::string s = std::string(text[10]) + typed;
                    change_text(10, s.c_str());
                }
            }
            else if(p==11) { // a number of triangles to remove
                if(key>='0' && key<='9') {
                    typed += key;
                    std::string s = std::string(text[11] + typed);
                    change_text(11, s.c_str());
                }
            }
        }
    }

    // adjust the menu when an object gets loaded/unloaded
    void ob_adjust_menu()
    {
        menu_pos_visible(2, 9, ob_loaded);
        menu_pos_active(3, ob_loaded);
        menu_pos_active(4, ob_loaded);
        menu_pos_active(5, ob_loaded);
        menu_pos_active(6, ob_loaded);
        menu_pos_active(7, ob_loaded);
        menu_pos_active(9, ob_loaded);
        
        menu_pos_visible(11, 13, ob_loaded);
        menu_pos_active(11, ob_loaded);
        menu_pos_active(12, ob_loaded);
        menu_pos_active(13, ob_loaded);
    }

    // updates the value of the percent of the triangles to remove
    void tr_perc_adjust_menu()
    {
        std::ostringstream ss;
        ss<<tr_perc;
        std::string s = std::string(text[10]) + ss.str();
        change_text(9, s.c_str());
    }

    void tr_num_adjust_menu()
    {
        std::ostringstream s;
        s<<text[11]<<tr_num;
        change_text(11,s.str().c_str());
    }

    void load_ob(std::string filename)
    {
        if(is_ply(filename)==false) {
            cout<<"error: file "<<filename<<" is of unknown format or it does not exist."
                <<endl<<flush;
            return;
        }

        if(jmesh!=NULL)
           delete jmesh;
        jmesh = new jeff::Mesh((char*)filename.c_str());
        if(jmesh->getNumVerts()>0 && jmesh->getNumTriangles()>0) 
        {
            jmesh->Normalize();
            cout<<"Loaded "<<typed<<endl<<flush;

            rebuild_jpmeshes(false);
            // remove the meshes of the old object, if any
            if(do_q==false && jpmesh_q!=NULL) {
                delete jpmesh_q;
                jpmesh_q=NULL;
            }
            if(do_qt==false && jpmesh_qt!=NULL) {
                delete jpmesh_qt;
                jpmesh_qt=NULL;
            }
            if(do_m==false && jpmesh_m!=NULL) {
                delete jpmesh_m;
                jpmesh_m=NULL;
            }
            if(do_s==false && jpmesh_s!=NULL) {
                delete jpmesh_s;
                jpmesh_s=NULL;
            }

            update_tr_num_from_perc();
            tr_num_adjust_menu();
            ob_name=filename;
            ob_loaded=true;
            display_ob=true;
            illuminated=true;
            polygon_mode=true;
            show_help=false;
            li.set_flat_shading();
            jcam.resetOrientation();
        }
        else 
        { // num verts == 0
            delete jmesh;
            jmesh=NULL;
            if(jpmesh_q!=NULL) {
                delete jpmesh_q;
                jpmesh_q=NULL;
            }
            if(jpmesh_qt!=NULL) {
                delete jpmesh_qt;
                jpmesh_qt=NULL;
            }
            if(jpmesh_m!=NULL) {
                delete jpmesh_m;
                jpmesh_m=NULL;
            }
            if(jpmesh_s!=NULL) {
                delete jpmesh_s;
                jpmesh_s=NULL;
            }

            // preserve the choice of methods as it is

            ob_loaded=false;
            cout<<"Failed to load \""<<filename<<"\" (or it was an empty object)."<<endl;
        }
        reshape(width,height);
        ob_adjust_menu();
    }

    void save_tr_percent(std::string percent)
    {
        std::istringstream ss(percent);
        float f;
        ss >> f;
        if(ss && f>0 && f<100)
            tr_perc = f;
    }

    void save_tr_num(std::string num)
    {
        std::istringstream ss(num);
        int n;
        ss >> n;
        if(ss && n>0 && n<get_min_num_triangles())
            tr_num = n;
    }

    void initOpenGL()
    {
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

        li.disable_all();
        //todo probably it's necessary to bring back the light's parameters
        // (they might have changed in another screen)
	// Enable lights and lighting
	glEnable(GL_LIGHT0); // default value is (1.0, 1.0, 1.0, 1.0)
	glEnable(GL_LIGHTING);

	glEnable(GL_CULL_FACE); // backface culling

        comp_type diff[3] = {0.8, 0.8, 0.8}; // grey
        comp_type spec[3] = {0, 0, 0};
        material<comp_type> m(diff,spec);
        m.shininess=128;
        m.set();

	//glShadeModel(GL_SMOOTH); // Gouraud shading
        li.set_flat_shading();
    }

    void display_jmesh(jeff::Mesh* mesh)
    {
        assert(mesh!=NULL);
        glBegin(GL_TRIANGLES);
        int end=mesh->getNumTriangles();
        for (int i=0; i<end; i++)
        {
            jeff::triangle t=mesh->getTri(i);
            float a[3];
            //if (jmesh->getTri(i, t) && t.isActive())
            {
                if (li.is_smooth_shading())
                {
                        const jeff::vertex& v1 = t.getVert1vertex();
                        const float* pFltV1Norm = v1.getArrayVertNorms();
                        glNormal3fv(pFltV1Norm);
                        const float* pFltArray1 = v1.getArrayVerts();
                        glVertex3fv(pFltArray1);
                        const jeff::vertex& v2 = t.getVert2vertex();
                        const float* pFltV2Norm = v2.getArrayVertNorms();
                        glNormal3fv(pFltV2Norm);
                        const float* pFltArray2 = v2.getArrayVerts();
                        glVertex3fv(pFltArray2);
                        const jeff::vertex& v3 = t.getVert3vertex();
                        const float* pFltV3Norm = v3.getArrayVertNorms();
                        glNormal3fv(pFltV3Norm);
                        const float* pFltArray3 = v3.getArrayVerts();
                        glVertex3fv(pFltArray3);

                        // flip it
                        a[0] = -pFltV3Norm[0];
                        a[1] = -pFltV3Norm[1];
                        a[2] = -pFltV3Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray3);
                        a[0] = -pFltV2Norm[0];
                        a[1] = -pFltV2Norm[1];
                        a[2] = -pFltV2Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray2);
                        a[0] = -pFltV1Norm[0];
                        a[1] = -pFltV1Norm[1];
                        a[2] = -pFltV1Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray1);
                }
                else // flat shaded
                {
                        const float* pFltArrayN = t.getNormal();
                        glNormal3fv(pFltArrayN);
                        const float* pFltArray1 = t.getVert1();
                        glVertex3fv(pFltArray1);
                        const float* pFltArray2 = t.getVert2();
                        glVertex3fv(pFltArray2);
                        const float* pFltArray3 = t.getVert3();
                        glVertex3fv(pFltArray3);

                        a[0] = -pFltArrayN[0];
                        a[1] = -pFltArrayN[1];
                        a[2] = -pFltArrayN[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray3);
                        glVertex3fv(pFltArray2);
                        glVertex3fv(pFltArray1);
                }
            }
        }
        glEnd();
    }

    void display_jmesh(jeff::PMesh* mesh)
    {
        assert(mesh!=NULL);
        glBegin(GL_TRIANGLES);
        for (int i=0; i<mesh->numTris(); i++)
        {
            jeff::triangle t;
            float a[3];
            if (mesh->getTri(i, t) && t.isActive())
            {
                if (li.is_smooth_shading())
                {
                        const jeff::vertex& v1 = t.getVert1vertex();
                        const float* pFltV1Norm = v1.getArrayVertNorms();
                        glNormal3fv(pFltV1Norm);
                        const float* pFltArray1 = v1.getArrayVerts();
                        glVertex3fv(pFltArray1);
                        const jeff::vertex& v2 = t.getVert2vertex();
                        const float* pFltV2Norm = v2.getArrayVertNorms();
                        glNormal3fv(pFltV2Norm);
                        const float* pFltArray2 = v2.getArrayVerts();
                        glVertex3fv(pFltArray2);
                        const jeff::vertex& v3 = t.getVert3vertex();
                        const float* pFltV3Norm = v3.getArrayVertNorms();
                        glNormal3fv(pFltV3Norm);
                        const float* pFltArray3 = v3.getArrayVerts();
                        glVertex3fv(pFltArray3);

                        // flip it
                        a[0] = -pFltV3Norm[0];
                        a[1] = -pFltV3Norm[1];
                        a[2] = -pFltV3Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray3);
                        a[0] = -pFltV2Norm[0];
                        a[1] = -pFltV2Norm[1];
                        a[2] = -pFltV2Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray2);
                        a[0] = -pFltV1Norm[0];
                        a[1] = -pFltV1Norm[1];
                        a[2] = -pFltV1Norm[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray1);
                }
                else
                {
                        const float* pFltArrayN = t.getNormal();
                        glNormal3fv(pFltArrayN);
                        const float* pFltArray1 = t.getVert1();
                        glVertex3fv(pFltArray1);
                        const float* pFltArray2 = t.getVert2();
                        glVertex3fv(pFltArray2);
                        const float* pFltArray3 = t.getVert3();
                        glVertex3fv(pFltArray3);

                        a[0] = -pFltArrayN[0];
                        a[1] = -pFltArrayN[1];
                        a[2] = -pFltArrayN[2];
                        glNormal3fv(a);
                        glVertex3fv(pFltArray3);
                        glVertex3fv(pFltArray2);
                        glVertex3fv(pFltArray1);
                }
            }
        }
        glEnd();
    }

    void build_jpmesh(jeff::PMesh::EdgeCost ec)
    {
        //cout<<ec<<flush<<endl;
        switch(ec) {
            case jeff::PMesh::QUADRIC:
                if(jpmesh_q!=NULL)
                    delete jpmesh_q;
                jpmesh_q = new jeff::PMesh(jmesh,ec);
                assert(jpmesh_q->numTris()>0);
                break;
            case jeff::PMesh::QUADRICTRI:
                if(jpmesh_qt!=NULL)
                    delete jpmesh_qt;
                jpmesh_qt = new jeff::PMesh(jmesh,ec);
                assert(jpmesh_qt->numTris()>0);
                break;
            case jeff::PMesh::MELAX:
                if(jpmesh_m!=NULL)
                    delete jpmesh_m;
                jpmesh_m = new jeff::PMesh(jmesh,ec);
                assert(jpmesh_m->numTris()>0);
                break;
            case jeff::PMesh::SHORTEST:
                if(jpmesh_s!=NULL)
                    delete jpmesh_s;
                jpmesh_s = new jeff::PMesh(jmesh,ec);
                assert(jpmesh_s->numTris()>0);
                break;
            default:
                ;
        }
        
        cout<<"Progressive mesh for \""<<jeff::PMesh::getEdgeCostDesc(ec)
            <<"\" method built"<<endl<<flush;
    }

    // a NOTICE: be carefull when you call the function just after the load of
    // a new object, because the meshes of unselected methods will be kept 
    // (and if call this function later on with check_changes==true,
    // then there might be a case that a mesh of an old object stays
    // if that mesh haven't changed, which will result in inconsistency
    // with the present object and it will lead to a fault)
    void rebuild_jpmeshes(bool check_changes=true)
    {
                                // checks whether a mesh has changed
                                // if not, it will not be rebuilt
            if(do_q && (check_changes==false ||
                          // helps when a method is chosen that has 
                          // never been chosen previously
                        jpmesh_q==NULL ||  
                        jpmesh_q->numTris() != jpmesh_q->numVisTris()
                       )
              )
                build_jpmesh(jeff::PMesh::QUADRIC);
            if(do_qt && (check_changes==false ||
                         jpmesh_qt==NULL ||
                         jpmesh_qt->numTris() != jpmesh_qt->numVisTris()
                        )
              )
                build_jpmesh(jeff::PMesh::QUADRICTRI);
            if(do_m && (check_changes==false ||
                        jpmesh_m==NULL ||
                        jpmesh_m->numTris() != jpmesh_m->numVisTris()
                       )
              )
                build_jpmesh(jeff::PMesh::MELAX);
            if(do_s && (check_changes==false ||
                        jpmesh_s==NULL ||
                        jpmesh_s->numTris() != jpmesh_s->numVisTris()
                       )
              )
                build_jpmesh(jeff::PMesh::SHORTEST);
    }

                        // numbers start from 0 (since we are in C++)
    void display_num_jmesh(int num)
    {
        int n=-1;

        if(do_q) n++;
        if(num==n) {
            display_jmesh(jpmesh_q);
            return;
        }
        if(do_qt) n++;
        if(num==n) {
            display_jmesh(jpmesh_qt);
            return;
        }
        if(do_m) n++;
        if(num==n) {
            display_jmesh(jpmesh_m);
            return;
        }
        if(do_s) n++;
        if(num==n) {
            display_jmesh(jpmesh_s);
            return;
        }
        if(do_orig) n++;
        if(num==n) {
            display_jmesh(jmesh);
            return;
        }
        assert(false);
    }

    std::string get_method_name(int num)
    {
        int n=-1;

        if(do_q) n++;
        if(num==n)
            return "q";
        
        if(do_qt) n++;
        if(num==n)
            return "qt";

        if(do_m) n++;
        if(num==n)
            return "m";

        if(do_s) n++;
        if(num==n)
            return "s";

        if(do_orig) n++;
        if(num==n)
            return "orig";

        assert(false);
    }

    int get_num_triangles(int num)
    {
        int n=-1;

        if(do_q) n++;
        if(num==n) {
            assert(jpmesh_q);
            return jpmesh_q->numVisTris();
        }
        if(do_qt) n++;
        if(num==n) {
            assert(jpmesh_qt);
            return jpmesh_qt->numVisTris();
        }
        if(do_m) n++;
        if(num==n) {
            assert(jpmesh_m);
            return jpmesh_m->numVisTris();
        }
        if(do_s) n++;
        if(num==n) {
            assert(jpmesh_s);
            return jpmesh_s->numVisTris();
        }
        if(do_orig) n++;
        if(num==n) {
            assert(jmesh);
            return jmesh->getNumTriangles();
        }
        assert(false);
        return -1; 
    }

        /*! includes only chosen methods' objects in searching for the minimum
        // (if any new method is chosen, then all the meshes will be rebuilt)
        //
        // if the meshes are rebuilt, then the minimum found earlier by this 
        // method will be smaller (since new meshes are equal to the original object),
        // which is okay
        */
    int get_min_num_triangles()
    {
        int min=jmesh->getNumTriangles(); // the maximum; if nothing is chosen (nothing
                                // is display), or just the original object is chosen, then
                                // the minimum is the total number of triangles of the orig mesh)
        int end=0;
        jeff::PMesh* m[4];
        if(do_q)//jpmesh_q!=NULL) 
            m[end++]=jpmesh_q;
        if(do_qt)//jpmesh_qt!=NULL)
            m[end++]=jpmesh_qt;
        if(do_m)//jpmesh_m!=NULL)
            m[end++]=jpmesh_m;
        if(do_s)//jpmesh_s!=NULL)
            m[end++]=jpmesh_s;

        for(int i=0; i<end; i++)
            if(m[i]->numVisTris() < min)
                min = m[i]->numVisTris();
        return min;
    }

    std::string get_num_triangles_str(int num)
    {
        int n=get_num_triangles(num);
        std::ostringstream os;
        os<<n;
        return os.str();
    }

    void update_tr_num_from_perc()
    {
        if(num_chosen_methods()<1) 
            // take specified percent of the original mesh'es triangles
            tr_num=int(tr_perc/100*jmesh->getNumTriangles());
        else
            // take specified percent of the first active method's mesh
            tr_num=int(tr_perc/100*get_num_triangles(0));
    }

    void update_tr_perc_from_num()
    {
        float tp;
        if(num_chosen_methods()<1)  // see the notice above
            tp = ((float)tr_num)/jmesh->getNumTriangles();
        else
            tp = ((float)tr_num)/get_num_triangles(0);

        tp *= 100; //now it's in percents
        tr_perc = ((int)(tp*100))/100.0; // take just two digits after the dot
    }

    void collapse()
    {
        int n=num_chosen_methods();
        if(n==0 || n==1 && do_orig) // nothing to do
            return;

        bool ret=true;
        int i;
        int end=tr_num/2; // one edge collapse normally collapses 2 triangles
        for(i=0; ret && i<end; i++) {
            if(do_q)
                ret = ret && jpmesh_q->collapseEdge();
            if(do_qt)
                ret = ret && jpmesh_qt->collapseEdge();
            if(do_m)
                ret = ret && jpmesh_m->collapseEdge();
            if(do_s)
                ret = ret && jpmesh_s->collapseEdge();
        }
        if(ret)
            cout<<end<<" edges collapsed"<<endl<<flush;
        else
            cout<<i-1<<" edges collapsed okay and then an error occured "
                <<"(at least in one of the chosen methods); "
                <<"isn't the object collapsed totally?"<<endl<<flush;
    }

    void split()
    {
        int n=num_chosen_methods();
        if(n==0 || n==1 && do_orig) // nothing to do
            return;
        bool ret=true;

        int i;
        int end=tr_num/2;
        for(i=0; ret && i<end; i++) {
            if(do_q)
                ret = ret && jpmesh_q->splitVertex();
            if(do_qt)
                ret = ret && jpmesh_qt->splitVertex();
            if(do_m)
                ret = ret && jpmesh_m->splitVertex();
            if(do_s)
                ret = ret && jpmesh_s->splitVertex();
        }
        if(ret)
            cout<<end<<" vertices split"<<endl<<flush;
        else
            cout<<i-1<<" vertices split fine and then an error occured "
                <<"(at least in one of the chosen methods); "
                <<"isn't the object entirely rescontructed?"<<endl<<flush;
    }

    void grey()
    {
        // Make everything grey
        glColor3ub(128, 128, 128);
    }

    bool is_ply(std::string filename)
    {
        std::ifstream is;
        std::string word;
        bool ret;
        is.open(filename.c_str());
        if(!is)
            return false;

        is>>word;
        if(is && word=="ply")
            ret=true;
        else
            ret=false;
        is.close();
        return ret;
    }

    std::string int2str(int i)
    {
        std::ostringstream os;
        os<<i;
        return os.str();
    }

    int get_2dig_percent(int nvert)
    {
        if(jmesh!=NULL) {
            float f=(float)nvert/jmesh->getNumTriangles();
            return (int)(f*10000)/100;
        }
        else
            return -1;
    }

    std::string get_2dig_percent_str(int nvert)
    {
        return int2str(get_2dig_percent(nvert));
    }


    private:
    static const char* text[];
    static const int yinc_text[];
    static const int xoffset_text[];
    static bool show_text[];
    static bool menu_pos[];
    static const int text_count;

    static const char* help_str[];
    static const int help_count;

    int width;
    int height;

    bool ob_loaded;

    bool typing;
    std::string typed;

    jeff::Mesh* jmesh;
                        // simplification methods
    jeff::PMesh* jpmesh_q; // quadric
    jeff::PMesh* jpmesh_qt; // quadric weighted by triangle area
    jeff::PMesh* jpmesh_m; // Stan Melax's
    jeff::PMesh* jpmesh_s; // shortest edge

    lighting li;

    bool do_q;
    bool do_qt;
    bool do_m;
    bool do_s;
    bool do_orig;

    // percent of the triangles of the original mesh that will be removed
    float tr_perc;
    // a number of the triangles that will be removed in the next collapse
    int tr_num;

    bool display_ob;
    bool illuminated;
    bool polygon_mode; // false ~= wireframe, true ~= normal

    jeff_camera jcam;

    std::string ob_name;

    static const short START_ACT_MENU_POS;// = 0;
    //static const bool HELP_SHOWED_ON_START;// = false;
};


#endif // LOD_COMPARE_DISP_H
